package com.devops.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.devops.entity.AutoYesUserInfo;
import com.devops.entity.req.CmdReq;
import com.devops.entity.req.SshInfo;
import com.devops.entity.resp.ErrorReason;
import com.devops.service.SshService;
import com.devops.utils.ssh.SshConst;
import com.devops.utils.ssh.SshRestoreUtil;
import com.devops.utils.error.ReturnErrorException;
import com.devops.utils.error.ReturnResultType;
import com.devops.utils.websocket.WebSocket;
import com.jcraft.jsch.*;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@Service
public class SshServiceImpl implements SshService {

    @Override
    public List<SshInfo> getSshConnect() throws ReturnErrorException {
        return SshRestoreUtil.getAllSession();
    }

    @Override
    public String SshConnect(SshInfo sshInfo) throws ReturnErrorException {
        String ip = sshInfo.getHostIp();
        if (SshRestoreUtil.hasValue(ip)) {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("该ip已经连接ssh端口"));
        }
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(sshInfo.getUserName(), ip, sshInfo.getHostPort());
            session.setPassword(sshInfo.getPassWord());
            session.setConfig("StrictHostKeyChecking", "no");
            // 跳过yes认证
            AutoYesUserInfo autoYesUserInfo = new AutoYesUserInfo();
            session.setUserInfo(autoYesUserInfo);
            session.connect();
            SshRestoreUtil.setValue(ip, session);
            return "连接成功";
        } catch (JSchException e) {
            String error = e.getMessage();
            if (SshConst.AUTH_FAIL.equals(error)) {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:无权限"));
            } else if (SshConst.TIMED_OUT.equals(error)) {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:连接超时"));
            } else {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public SshInfo getSshConnect(String hostIp) throws ReturnErrorException {
        Session session = SshRestoreUtil.getSession(hostIp);
        if (!ObjectUtil.isEmpty(session)) {
            SshInfo sshInfo = new SshInfo();
            sshInfo.setHostIp(session.getHost());
            sshInfo.setHostPort(session.getPort());
            sshInfo.setUserName(session.getUserName());
            return sshInfo;
        } else {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
        }
    }

    @Override
    public void deleteConnect(String hostIp) throws ReturnErrorException {
        SshRestoreUtil.remove(hostIp);
    }

    @Override
    public String cmd(CmdReq cmdReq) throws Exception {
        try {
            Session session = SshRestoreUtil.getSession(cmdReq.getHostIp());
            if (!ObjectUtil.isEmpty(session)) {
                ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
                InputStream in = channelExec.getInputStream();
                channelExec.setCommand(cmdReq.getCmd());
                channelExec.setErrStream(System.err);
                channelExec.connect();

                // 等待命令执行完毕
                while (!channelExec.isClosed()) {
                    Thread.sleep(100);
                }

                String s = IOUtils.toString(in, "UTF-8");

                channelExec.disconnect();
                in.close();
                return s;
            } else {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
            }
        } catch (Exception e) {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
        }
    }

    @Override
    public void cmd(String hostIp, String cmd) throws Exception {
        try {
            Session session = SshRestoreUtil.getSession(hostIp);
            if (!ObjectUtil.isEmpty(session)) {
                // 命令返回的结果
                StringBuffer successBuffer = new StringBuffer();
                ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
                channelExec.setCommand(cmd);
                channelExec.connect();
                InputStream success = channelExec.getInputStream();
                InputStream error = channelExec.getErrStream();

                // 等待命令执行完毕
                while (!channelExec.isClosed()) {
                    addString(successBuffer, success, error);
                    Thread.sleep(100);
                }
                addString(successBuffer, success, error);

                channelExec.disconnect();
            } else {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
            }
        } catch (Exception e) {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
        }
    }

    @Override
    public void sftp(String hostIp, String resourceFile, String dir) throws Exception {
        if (!SshRestoreUtil.hasValueSftp(hostIp)) {
            SshRestoreUtil.setValueChannelSftp(hostIp);
        }
        FileInputStream fis = null;
        try {
            ClassPathResource resource = new ClassPathResource(resourceFile);
            fis = new FileInputStream(resource.getFile());
            ChannelSftp channelSftp = SshRestoreUtil.getSftp(hostIp);
            createSftpFile(channelSftp, dir);
            channelSftp.cd(dir);
            channelSftp.put(fis, resource.getFile().getName());
        } catch (Exception e) {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("文件上传失败"));
        }
    }

    @Override
    public void shell(String hostIp, String fileName) throws Exception {
        if (!SshRestoreUtil.hasValueSftp(hostIp)) {
            SshRestoreUtil.setValueChannelSftp(hostIp);
        }
        ChannelSftp channelSftp = SshRestoreUtil.getSftp(hostIp);
        if (!isSftpFileExist(channelSftp, fileName)) {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason( fileName + "该文件不存在"));
        }
        Session session = SshRestoreUtil.getSession(hostIp);
        if (!ObjectUtil.isEmpty(session)) {
            ChannelExec channel = null;
            try {
                StringBuffer successBuffer = new StringBuffer();
                channel = (ChannelExec) session.openChannel("exec");
                channel.setCommand("sh " + fileName);
                channel.connect();
                InputStream success = channel.getInputStream();
                InputStream error = channel.getErrStream();

                // 等待命令执行完毕
                while (!channel.isClosed()) {
                    addString(successBuffer, success, error);
                    Thread.sleep(100);
                }
                addString(successBuffer, success, error);

            } catch (Exception e) {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("执行文件失败"));
            } finally {
                if (channel != null) {
                    channel.disconnect();
                }
            }
        } else {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("连接失败:未知异常"));
        }
    }

    public static void addString (StringBuffer outBuffer, InputStream success, InputStream error) throws IOException {
        byte[] bytes = new byte[1024];
        while (success.available() > 0) {
            int i = success.read(bytes, 0, 1024);
            if (i < 0) {
                break;
            }
            sendWebsocket(bytes, i, outBuffer);
        }
        while (error.available() > 0) {
            int i = error.read(bytes, 0, 1024);
            if (i < 0) {
                break;
            }
           sendWebsocket(bytes, i, outBuffer);
        }
    }

    private static void sendWebsocket(byte[] bytes, int i, StringBuffer outBuffer) throws IOException {
        String str = new String(bytes, 0, i);
        WebSocket.sendMessage(str);
        outBuffer.append(str);
    }

    private static void createSftpFile(ChannelSftp channelSftp, String dir) throws Exception {
        try {
            channelSftp.cd(dir);
            channelSftp.ls(dir);
        } catch (SftpException e) {
            try {
                channelSftp.mkdir(dir);
            } catch (SftpException e1) {
                throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason("创建文件夹失败"));
            }
        }
    }

    private static boolean isSftpFileExist(ChannelSftp channelSftp, String dir) throws Exception {
        try {
            channelSftp.lstat(dir);
            return true;
        } catch (SftpException e) {
            throw new ReturnErrorException(ReturnResultType.OPERATOR_ERROR, new ErrorReason( dir+ "该文件不存在"));
        }
    }

}
